---
title: "Data Mapping with Workflow Variables | Kastrax Examples"
description: "Learn how to use workflow variables to map data between steps in Kastrax workflows."
---

# Data Mapping with Workflow Variables ✅

This example demonstrates how to use workflow variables to map data between steps in a Kastrax workflow.

## Use Case: User Registration Process ✅

In this example, we'll build a simple user registration workflow that:

1. Validates user input
1. Formats the user data
1. Creates a user profile

## Implementation ✅

```typescript showLineNumbers filename="src/kastrax/workflows/user-registration.ts" copy
import { Step, Workflow } from "@kastrax/core/workflows";
import { z } from "zod";

// Define our schemas for better type safety
const userInputSchema = z.object({
  email: z.string().email(),
  name: z.string(),
  age: z.number().min(18),
});

const validatedDataSchema = z.object({
  isValid: z.boolean(),
  validatedData: z.object({
    email: z.string(),
    name: z.string(),
    age: z.number(),
  }),
});

const formattedDataSchema = z.object({
  userId: z.string(),
  formattedData: z.object({
    email: z.string(),
    displayName: z.string(),
    ageGroup: z.string(),
  }),
});

const profileSchema = z.object({
  profile: z.object({
    id: z.string(),
    email: z.string(),
    displayName: z.string(),
    ageGroup: z.string(),
    createdAt: z.string(),
  }),
});

// Define the workflow
const registrationWorkflow = new Workflow({
  name: "user-registration",
  triggerSchema: userInputSchema,
});

// Step 1: Validate user input
const validateInput = new Step({
  id: "validateInput",
  inputSchema: userInputSchema,
  outputSchema: validatedDataSchema,
  execute: async ({ context }) => {
    const { email, name, age } = context;

    // Simple validation logic
    const isValid = email.includes('@') && name.length > 0 && age >= 18;

    return {
      isValid,
      validatedData: {
        email: email.toLowerCase().trim(),
        name,
        age,
      },
    };
  },
});

// Step 2: Format user data
const formatUserData = new Step({
  id: "formatUserData",
  inputSchema: z.object({
    validatedData: z.object({
      email: z.string(),
      name: z.string(),
      age: z.number(),
    }),
  }),
  outputSchema: formattedDataSchema,
  execute: async ({ context }) => {
    const { validatedData } = context;

    // Generate a simple user ID
    const userId = `user_${Math.floor(Math.random() * 10000)}`;

    // Format the data
    const ageGroup = validatedData.age < 30 ? "young-adult" : "adult";

    return {
      userId,
      formattedData: {
        email: validatedData.email,
        displayName: validatedData.name,
        ageGroup,
      },
    };
  },
});

// Step 3: Create user profile
const createUserProfile = new Step({
  id: "createUserProfile",
  inputSchema: z.object({
    userId: z.string(),
    formattedData: z.object({
      email: z.string(),
      displayName: z.string(),
      ageGroup: z.string(),
    }),
  }),
  outputSchema: profileSchema,
  execute: async ({ context }) => {
    const { userId, formattedData } = context;

    // In a real app, you would save to a database here

    return {
      profile: {
        id: userId,
        ...formattedData,
        createdAt: new Date().toISOString(),
      },
    };
  },
});

// Build the workflow with variable mappings
registrationWorkflow
  // First step gets data from the trigger
  .step(validateInput, {
    variables: {
      email: { step: 'trigger', path: 'email' },
      name: { step: 'trigger', path: 'name' },
      age: { step: 'trigger', path: 'age' },
    }
  })
  // Format user data with validated data from previous step
  .then(formatUserData, {
    variables: {
      validatedData: { step: validateInput, path: 'validatedData' },
    },
    when: {
      ref: { step: validateInput, path: 'isValid' },
      query: { $eq: true },
    },
  })
  // Create profile with data from the format step
  .then(createUserProfile, {
    variables: {
      userId: { step: formatUserData, path: 'userId' },
      formattedData: { step: formatUserData, path: 'formattedData' },
    },
  })
  .commit();

export default registrationWorkflow;
```

## How to Use This Example ✅

1. Create the file as shown above
2. Register the workflow in your Kastrax instance
3. Execute the workflow:

```bash
curl --location 'http://localhost:4111/api/workflows/user-registration/start-async' \
     --header 'Content-Type: application/json' \
     --data '{
       "email": "user@example.com",
       "name": "John Doe",
       "age": 25
     }'
```

## Key Takeaways ✅

This example demonstrates several important concepts about workflow variables:

1. **Data Mapping**: Variables map data from one step to another, creating a clear data flow.

2. **Path Access**: The `path` property specifies which part of a step's output to use.

3. **Conditional Execution**: The `when` property allows steps to execute conditionally based on previous step outputs.

4. **Type Safety**: Each step defines input and output schemas for type safety, ensuring that the data passed between steps is properly typed.

5. **Explicit Data Dependencies**: By defining input schemas and using variable mappings, the data dependencies between steps are made explicit and clear.

For more information on workflow variables, see the [Workflow Variables documentation](../../docs/workflows/variables.mdx).
